import Cesium from '../Ces/Cesium'
import WeLibrary from './WeLibrary'
import WeDisplayShaders from './WeDisplayShaders'

function buildCanvas(valueAndColorRamp, context) {
  if (!Cesium.defined(valueAndColorRamp.canvas)) {
    valueAndColorRamp.canvas = document.createElement('canvas')
    valueAndColorRamp.canvas.width = 100
    valueAndColorRamp.canvas.height = 10
  }

  WeLibrary.drawTransferCanvas(valueAndColorRamp.canvas, valueAndColorRamp)

  return new Cesium.Texture({
    context,
    source: valueAndColorRamp.canvas,
    pixelFormat: Cesium.PixelFormat.RGBA,
  })
}

function buildImage(valueAndColorRamp, context) {
  const valueRamp = valueAndColorRamp.ValueRamp
  const colorRamp = valueAndColorRamp.ColorRamp

  const colorCount = colorRamp.length
  const minValue = valueRamp[0]
  const maxValue = valueRamp[colorCount - 1]

  let scale = 1

  for (let i = 0; i < valueRamp.length; i++) {
    if (valueRamp[i] - Math.floor(valueRamp[i]) > 0.0) {
      scale = 10
    }
  }

  const width = Math.ceil(maxValue - minValue) * scale
  const height = 2
  const arrayBufferView = new Uint8Array(width * height * 4)

  for (let i = 1; i < colorCount; i++) {
    const value0 = valueRamp[i - 1]
    const value1 = valueRamp[i]
    const rgba = colorRamp[i]
    const percent0 = Math.floor((value0 - minValue) * scale)
    const percent1 = Math.floor((value1 - minValue) * scale)

    for (let j = percent0; j < percent1; j++) {
      let idx = j * 4

      arrayBufferView[idx] = percent0
      arrayBufferView[idx + 1] = percent1
      arrayBufferView[idx + 3] = 255

      idx += width * 4
      arrayBufferView[idx] = rgba[0]
      arrayBufferView[idx + 1] = rgba[1]
      arrayBufferView[idx + 2] = rgba[2]
      arrayBufferView[idx + 3] = rgba[3]
    }
  }

  return new Cesium.Texture({
    context,
    source: { width, height, arrayBufferView },
    pixelFormat: Cesium.PixelFormat.RGBA,
    pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE,
    sampler: new Cesium.Sampler({
      wrapS: Cesium.TextureWrap.CLAMP_TO_EDGE,
      wrapT: Cesium.TextureWrap.CLAMP_TO_EDGE,
      minificationFilter: Cesium.TextureMinificationFilter.NEAREST,
      magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
    }),
  })
}

export type WeDisplayLegendOptions = {
  valueAndColorRamp?: any
}

class WeDisplayLegend {
  dirty
  tfTexture
  _valueAndColorRamp
  defaultTexture: any
  constructor(options: WeDisplayLegendOptions) {
    if (!options) {
      options = Cesium.Frozen.EMPTY_OBJECT
    }
    this.dirty = false
    this.tfTexture = undefined
    this.valueAndColorRamp = options.valueAndColorRamp
  }

  set valueAndColorRamp(value) {
    this._valueAndColorRamp = value
    this.dirty = true
  }

  get valueAndColorRamp() {
    return this._valueAndColorRamp
  }

  get texture() {
    return this.tfTexture || this.defaultTexture
  }

  getSamplerDelcare() {
    return `
    #ifdef ${WeDisplayShaders.WeDefineUseLegend}
      uniform sampler2D u_TFTexture;
    #endif
   `
  }

  setupUniformMap(uniformMap) {
    // if (this.valueAndColorRamp) {
    uniformMap.u_TFTexture = () => this.texture
    // }
  }

  setupUniform(uniforms) {
    if (this.valueAndColorRamp) {
      uniforms.u_TFTexture = this.texture
    }
  }

  pushDefines(defines) {
    if (Cesium.defined(this.valueAndColorRamp)) {
      defines.push(WeDisplayShaders.WeDefineUseLegend)
    }
  }

  build(context) {
    const valueAndColorRamp = this._valueAndColorRamp
    if (!valueAndColorRamp) return

    let texture

    if (typeof (valueAndColorRamp) === 'string') {
      const url = valueAndColorRamp

      const that = this

      Cesium.Resource.fetchImage(url).then((image) => {
        that.tfTexture = that.tfTexture && that.tfTexture.destroy()
        that.tfTexture = new Cesium.Texture({
          context,
          source: image,
          flipY: false,
        })
      })
    } else {
      if (valueAndColorRamp.linear ?? false) {
        texture = buildCanvas(valueAndColorRamp, context)
      } else {
        texture = buildImage(valueAndColorRamp, context)
      }
      this.tfTexture = this.tfTexture && this.tfTexture.destroy()
      this.tfTexture = texture
    }
  }

  update(frameState) {
    if (!frameState.passes.render) return
    if (!this.defaultTexture) {
      this.defaultTexture = frameState.context.defaultTexture
    }

    if (this.dirty) {
      this.dirty = false
      this.build(frameState.context)
    }
  }

  destroy() {
    this.tfTexture = this.tfTexture && this.tfTexture.destroy()

    return Cesium.destroyObject(this)
  }

  isdestroyed() {
    return false
  }
}

export default WeDisplayLegend
